#include "../Epsilon/STL.h"
#include "../Epsilon/types.h"
#include "../Epsilon/timer.h"
#include "console.h"
#include "command.h"

static eTimer timer;


/* TODO: Integrate this into eTB_Console... */
void
AppendText ( const char*                   szAppend,
             std::vector <eTB_Text *>&     text__,
             std::vector <eTB_TextLine *>& lines__ )
{
  eTB_Text*     pText     = new eTB_Text (szAppend);
  eTB_TextLine* pTextLine = NULL;
  bool          bNewLine  = false;

  text__.push_back (pText);

  /* If there are already lines, try to add this span to the last one... */
  if (lines__.size ()) {
    std::vector <eTB_TextLine *>::iterator line = (lines__.end () - 1);

    /* If the line _DOES NOT_ contain a newline, then add this span... */
    if (! (*line)->IsTerminated ()) {
      (*line)->InsertSpan (pText);
    }

    /* Otherwise, start a new span, for a new line. */
    else
      bNewLine = true;
  } else {
    /* Otherwise, create a new line. */
    bNewLine = true;
  }

  /* If we determined above that this text span requires a new line, then
       this is where the magic happens... */
  if (bNewLine) {
    pTextLine = new eTB_TextLine ();
    pTextLine->InsertSpan  (pText);
    lines__.push_back      (pTextLine);
  }
}

/* NOTE: This is _NOT_ thread safe. */
int
eTB_Console::printf (const char* szFormat, ...)
{
  va_list argv;

  Assert (szFormat != NULL, _T ("Invalid format string"));

  if (szFormat == NULL)
    return 0;

  /* Length of the output string thus far... */
  int len = 0;

  static char szOut     [4096];  // Limit formatted text    to  4 Kb...
  static char szTabExp  [32768]; // Limit tab expansion     to 32 Kb...
  static char szSubLine [32768]; // Limit newline expansion to 32 Kb...

  *szOut     = '\0';
  *szTabExp  = '\0';
  *szSubLine = '\0';

  va_start (argv, szFormat);
    len += vsnprintf (szOut, 4096, szFormat, argv);
  va_end (argv);

  const char _TABCHAR = ' '; /* Replace each '\t' with _TABSTOP-many of */
  const int  _TABSTOP = 5;   /*   these characters... */
  int              nt = 0;   /* <-- nt = Number of tabs expanded. */

  /* Expand Tabs. */
  for (int i = 0; i < len; i++) {
    if (szOut [i] != '\t') {
      szTabExp [i + (nt * (_TABSTOP - 1))] = szOut [i];
    } else {
      for (int j = 0; j < _TABSTOP; j++) {
        szTabExp [i + (nt * (_TABSTOP - 1)) + j] = _TABCHAR;
      }
      ++nt;
    }
  }

  const int tabbed_len = len + (nt * (_TABSTOP - 1));

  /* Terminate the new string at the correct location... */
  szTabExp [tabbed_len] = '\0';

  int num_trailing_newlines = 0;
  int num_leading_newlines  = 0;

  for (int i = 0; i < tabbed_len; i++) {
    if (szTabExp [tabbed_len - i - 1] == '\n')
      num_trailing_newlines++;
    else
      break;
  }

  if (num_trailing_newlines > 0) {
    szTabExp [tabbed_len - num_trailing_newlines] = '\0';
  }

  for (int i = 0; i < tabbed_len; i++) {
    if (szTabExp [i] == '\n')
      num_leading_newlines++;
    else
      break;
  }

  /* If the number of leading newlines is equal to the strlen, then let the
       trailing newline detection code take care of this... */
  if (num_leading_newlines > 0) {
    for (int i = 0; i < num_leading_newlines; i++) {
      AppendText ("\n", text_, lines_);
    }
  }

  bool  tok    = true;
  char* szLine = strtok (szTabExp + num_leading_newlines, "\n");

  /* No newlines (aside from leading and/or trailing)... */
  if (szLine == NULL) {
    szLine = szTabExp + num_leading_newlines;
    tok    = false; /* No embedded new-lines... */
  }

  /* For Each Line... */
  while (szLine != NULL) {
    const int szLine_len = strlen (szLine);

    strcpy (szSubLine, szLine);

    /* szSubLine is part of a string of text with one or more embedded
         newline characters... */
    if (tok) {
      szLine = strtok (NULL, "\n");

      /* Add a newline character, where the next sub-string begins. */
      if (szLine != NULL) {
        szSubLine [szLine_len]     = '\n';
        szSubLine [szLine_len + 1] = '\0';
      } else {
        szLine = NULL;
      }
    } else {
      szLine = NULL;
    }

    AppendText (szSubLine, text_, lines_);

    /* Triggered when we reach the end of the string, add any trailing
         newlines back into the text at this point... */
    if (szLine == NULL) {
      if (num_trailing_newlines > 0) {
        for (int i = 0; i < num_trailing_newlines; i++) {
          AppendText ("\n", text_, lines_);
        }
      }
    }
  }

  return len;
}

void
eTB_Console_StdOut::Display (void)
{
  std::vector <eTB_Text *>::const_iterator line      = text_.begin ();
  std::vector <eTB_Text *>::const_iterator last_line = text_.end   ();

  if ((line + last_printed) < last_line) {
    line = (line + last_printed);

    while (eSTL::IterValid (line, last_line)) {
      ::printf ("%s", (*line)->GetMessage ());

      ++last_printed;
      ++line;
    }

    /* Differs from last_printed, in that this keeps count of
        the number of times new data was displayed.

         * Messages are buffered before displaying, and so it is
             possible for many messages to be displayed in a single
              call to Display (...).

          <=> This helps evaluate the efficiency of the Display method.
    */
    ++display_count;
  }
}

int
eTB_Console_StdOut::scanf (const char* szFormat, ...)
{
  /* It's typical to print a prompt to the console before calling
       scanf (...), so make sure we update the console's output before
         scanning for new input. */
  Display ();

#ifndef WIN32
  va_list argv;
#endif

  Assert (szFormat != NULL, _T ("Invalid format string"));

  if (szFormat == NULL)
    return 0;

  int args = 0;

  /* This is the smart way of doing this... */
#ifndef WIN32
  va_start (argv, szFormat);
    args += vscanf (szFormat, argv);
  va_end (argv);
#else
  /* WIN32 is a mess, to keep things portable, we use a macro to
       solve this problem on that platform...

         -> The problem being, a lack of vscanf (...)
  */
  return -1;
#endif

  return args;
}

#include "../Epsilon/epsilon.h"

#include "../Epsilon/window.h"

#include "../Epsilon/Epsilon3D/Epsilon3D.h"
#include "../Epsilon/Epsilon3D/rendercontext.h"
#include "../Epsilon/Epsilon3D/viewport.h"


#include "states.h"

#include "../Epsilon/Epsilon3D/OpenGL.h"

#include "window.h"

#include "cli_parse/structs.h"
#include "cli_parse/flags.h"

#include "cli_parse/sector.h"
#include "cli_parse/room.h"

#include "cli_parse/level.h"

#include "cli_parse/render_batch.h"

#include "cli_parse/line.h"
#include "cli_parse/sector.h"
#include "cli_parse/room.h"
#include "cli_parse/level.h"

#include "cli_parse/util.h"

#include "cli_parse/render.h"
#include "cli_parse/util.h"

#include "font.h"

#include "input.h"
#include "../Epsilon/EpsilonInput/keyboard.h"

extern eFont* font;

#define g_Window states->window

eTB_Console_Render::eTB_Console_Render (void) : eTB_Console   (),
                                                anchor_line   (0),
                                                display_count (0),
                                                visible       (false),
                                                active        (false)
{
  states->input->InstallListener (this);

  repeat_delay_init = 250;
  repeat_delay      = 50;

  key_change  = 0;
  last_repeat = 0;

  last_command_num = 0;
  cursor_pos       = 0;

  memset (command_line, 0, sizeof (char) * 1024);
};

void
eTB_Console_Render::Display (void)
{
  Display (this);
}

static e3dVertexCache* console_vcache = NULL;
static e3dIndexCache*  console_icache = NULL;

static std::vector <GlyphVertexEx>  console_verts;
static std::vector <unsigned short> console_indices;

void
eTB_Console_Render::Display (eTB_Console* parent)
{
  /* Do nothing, unless the console is visible... */
  if (! IsVisible ())
    return;

  timer.tick ();

  if (key_change != 0) {
    bool repeat = false;

    /* If last_repeat is 0, then we are waiting for the initial
         repeat delay... */
    if (last_repeat == 0) {
      if ((timer.milliseconds () - key_change) > repeat_delay_init)
        repeat = true;
    } else { /* Shorter delay for successive repeats... */
      if ((timer.milliseconds () - key_change) > repeat_delay)
        repeat = true;
    }

    /* If repeat is true, we will issue a Key Press event to make
         the console behave as if the user had pressed the key again... */
    if (repeat) {
      OnKeyPress (last_key);
      last_repeat = timer.milliseconds ();
    }
  }


  struct OverlayVertex
  {
    float         pos   [4];
    unsigned char color [4];
  };

  OverlayVertex aOverlayVertices [4];

  OverlayVertex* pVert = aOverlayVertices;

  const float color = 0.5f;

  for (int i = 0; i < 4; i++) {
            aOverlayVertices [i].pos [2] = 0.0f;
            aOverlayVertices [i].pos [3] = 1.0f; // w = 1.0f
    (DWORD&)aOverlayVertices [i].color   = D3DCOLOR_COLORVALUE (0.0f, 0.0f, 0.0f, color);
  }

  /* Span 1/2 of the window (from top to bottom) */
  const float fSpanRatio  = 0.5f;
  const float fViewWidth  = (float)g_Window->viewport.Width  ();
  const float fViewHeight = (float)g_Window->viewport.Height ();

  pVert->pos [0] = 0.0f;
  pVert->pos [1] = fViewHeight * (1.0f - fSpanRatio);

  pVert++;

  pVert->pos [0] = 0.0f;
  pVert->pos [1] = fViewHeight;

  pVert++;

  pVert->pos [0] = fViewWidth;
  pVert->pos [1] = fViewHeight * (1.0f - fSpanRatio);

  pVert++;

  pVert->pos [0] = fViewWidth;
  pVert->pos [1] = fViewHeight;

  pVert = aOverlayVertices;

  cli_parse::states->render_context->SetCullFaces (e3dRenderStates::False);
  cli_parse::states->render_context->SetDepthTest (e3dRenderStates::False);

  glDisable (GL_TEXTURE_2D);
  glDisable (GL_COLOR_MATERIAL);

  /// Originally: GL_ZERO, GL_SRC_ALPHA   - Changed to "fix" texture coord debug overlay
  glBlendFunc          (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisableClientState (GL_COLOR_ARRAY);

  glPushAttrib   (GL_TRANSFORM_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT);

  glMatrixMode   (GL_MODELVIEW);
  glPushMatrix   ();
  glLoadIdentity ();

  glMatrixMode   (GL_PROJECTION);
  glPushMatrix   ();
  glLoadIdentity ();

  glOrtho (0, fViewWidth, 0, fViewHeight, -1.0f, 1.0f);

  glEnable (GL_BLEND);

  glBegin (GL_TRIANGLE_STRIP);

  glColor4f (0.0f, 0.0f, 0.0f, color);

  glVertex2f (aOverlayVertices [0].pos [0],
              aOverlayVertices [0].pos [1]);

  glVertex2f (aOverlayVertices [1].pos [0],
              aOverlayVertices [1].pos [1]);

  glVertex2f (aOverlayVertices [2].pos [0],
              aOverlayVertices [2].pos [1]);

  glVertex2f (aOverlayVertices [3].pos [0],
              aOverlayVertices [3].pos [1]);

  glEnd ();

  glDisable (GL_BLEND);

  //glMatrixMode (GL_PROJECTION); // Redundant
  glPopMatrix  ();

  glMatrixMode (GL_MODELVIEW);
  glPopMatrix  ();

  glPopAttrib  ();

  cli_parse::states->render_context->SetCullFaces (e3dRenderStates::True);
  cli_parse::states->render_context->SetDepthTest (e3dRenderStates::True);

  float x = 0.0f;
  float y = fViewHeight * fSpanRatio;

  ///
  /// XXX: Change Me!
  ///
  /// Used only to set a default font
  ///
  std::vector <eTB_Text *> parent_text          = parent->GetText   ();
  std::vector <eTB_Text *>::const_iterator line = parent_text.begin ();

  if (! console_vcache)
    console_vcache = g_Window->rc->CreateVertexCache ();
  if (! console_icache)
    console_icache = g_Window->rc->CreateIndexCache ();

  eFont* font = (*line)->GetFont ();

  y += (font->GetRowHeight () + 9.0f);


  int last_vtx = 0;


  /* As of 02/16/2011, it is not clear why this is necessary to properly draw
       console text in optimized builds... */
  if (true) {
    char*  szEditedCommand = strdup (command_line);
    size_t len             = strlen (szEditedCommand);

    if (cursor_pos > len)
      cursor_pos = len;

    *(szEditedCommand + len - cursor_pos) = '\0';

    x += (font->StringWidth (szEditedCommand));

    free ((char *)szEditedCommand);

//    glColor4ubv ((unsigned char *)&dwGreen);
    if (states->r_batch_print)
      font->PrintToArray (" ", x, y - 1.0f, 0, last_vtx, console_verts, console_indices);
    else
      font->Print (" ", x, y - 1.0f, console_vcache, console_icache);

    x = 0.0f;
  }

  //
  // Print the Command Prompt first.
  //
  if (IsActive ())
  {
    const DWORD dwGreen  = 0xff00ff00;
    const DWORD dwRed    = 0xff0000ff;
    const DWORD dwYellow = dwGreen | dwRed;

    glColor4ubv ((unsigned char *)&dwRed);

    if (states->r_batch_print)
      font->PrintToArray ("[cli_parse]# ", x, y, dwRed, last_vtx, console_verts, console_indices);
    else
      font->Print ("[cli_parse]# ", x, y, console_vcache, console_icache);

    x += (font->StringWidth ("[cli_parse]# "));

    glColor4ubv ((unsigned char *)&dwYellow);

    if (states->r_batch_print)
      font->PrintToArray (command_line, x, y, dwYellow, last_vtx, console_verts, console_indices);
    else
      font->Print (command_line,    x, y, console_vcache, console_icache);

//      x_ += (font->StringWidth (command_line));

    static int  last_blink  = 0;
    static bool draw_cursor = false;
    timer.tick ();

    if ((timer.milliseconds () - last_blink) > 250) {
      draw_cursor = ! draw_cursor;
      last_blink  = timer.milliseconds ();
    }

    if (draw_cursor) {
      char*  szEditedCommand = strdup (command_line);
      size_t len             = strlen (szEditedCommand);

      if (cursor_pos > len)
        cursor_pos = len;

      *(szEditedCommand + len - cursor_pos) = '\0';

      x += (font->StringWidth (szEditedCommand));

      free ((char *)szEditedCommand);

      glColor4ubv ((unsigned char *)&dwGreen);

      if (states->r_batch_print)
        font->PrintToArray ("_", x, y - 1.0f, dwGreen, last_vtx, console_verts, console_indices);
      else
        font->Print ("_", x, y - 1.0f, console_vcache, console_icache);
    }

    // Begin drawing text _after_ the optional command prompt...
    y += (font->GetRowHeight () + 4.0f);
  }

  x = 0.0f;

  std::vector <eTB_TextLine *>&
            lines     = parent->GetTextLines ();
  const int num_lines = lines.size           ();

  /* NOTE: We render the lines in reverse order, so that the height of each
             line does not need to be known ahead of time. The signal to
               stop rendering comes when the y-coordinate exceedes its max.
  */
  for (int i = (num_lines - 1); i > 0; i--)
  {
    // Stop trying to print lines if we've hit the top of the console...
    if (y > fViewHeight)
      break;

    // Don't print lines _after_ the anchor line...
    if (i >= (num_lines - anchor_line))
      continue;

    const int num_spans = lines [i]->getNumSpans ();

    for (unsigned int j = 0; j < lines [i]->getNumSpans (); j++)
    {
      eTB_Text* pLine = lines [i]->getSpan (j);

      eFont*      font      = pLine->GetFont    ();
      const char* szMessage = pLine->GetMessage ();
      int         str_len   = strlen (szMessage);

      DWORD dwColor = (*line)->GetColor ();
      glColor4ubv ((unsigned char *)&dwColor);

      if (states->r_batch_print)
        font->PrintToArray ((char *)szMessage, x, y, dwColor, last_vtx, console_verts, console_indices);
      else
        font->Print ((char *)szMessage, x, y, console_vcache, console_icache);

      x += font->StringWidth ((char *)szMessage);
    }

    y += (font->GetRowHeight () + 4.0f);
    x = 0.0f;
  }

  if (states->r_batch_print)
    font->FinishArrayPrint (last_vtx, console_verts, console_indices);

  if (y < fViewHeight)
    anchor_line--;


#if 0
    /* Differs from last_printed, in that this keeps count of
        the number of times new data was displayed.

         * Messages are buffered before displaying, and so it is
             possible for many messages to be displayed in a single
              call to Display (...).

          <=> This helps evaluate the efficiency of the Display method.
    */
    ++display_count;
#endif
}

int
eTB_Console_Render::scanf (const char* szFormat, ...)
{
  return 0;
}


bool
eTB_Console_Render::OnKeyRelease (Epsilon::Keyboard::Key key)
{
  key_change  = 0;
  last_repeat = 0;

  return true;
}

///
/// TODO: Edit Mode (Insert / Delete)
///
void
EditCommandLine (unsigned int& edit_pos, char* string, char edit_char)
{
  bool backspace = false;

  /* '\b' means backspace, which deletes the character _before_ the current
       edit_pos. */
  if (edit_char == '\b')
    backspace = true;

  if (edit_pos != 0)
  {
    int   pos      = edit_pos;
    int   iLen     = strlen (string);
    char* szBefore = strdup (string);
    char* szEx     = strdup (string);
    char* szAfter  = (szBefore + iLen - edit_pos - 1);

    /* Backspace will delete the character _before_ the edit pos... */
    if (backspace)
      pos++;

    if (pos > iLen)
      return;

    *(szBefore + iLen - pos) = '\0';
    szAfter = (szEx + iLen - pos);

    if (! backspace)
      snprintf (string, 1023, "%s%c%s", szBefore, edit_char, szAfter);
    else
      snprintf (string, 1023, "%s%s", szBefore, (szAfter + 1));

    free (szBefore);
    free (szEx);
  } else {
    if (! backspace)
      snprintf (string, 1023, "%s%c", string, edit_char);
    else
      *(string + strlen (string) - 1) = '\0';
  }
}

#include "sound.h" // OpenALSource ...

char*
first_alpha_num (char* szCmd)
{
  size_t len = strlen (szCmd);

  for (size_t i = 0; i < len; i++) {
    if (! (iscntrl (szCmd [i]) ||
           isspace (szCmd [i]))   )
      return &szCmd [i];
  }

  return NULL;
}

bool
eTB_Console_Render::OnKeyPress (Epsilon::Keyboard::Key key)
{
  /* Load Sounds... */
  static OpenALSource* activate_sound   = NULL;
  static OpenALSource* deactivate_sound = NULL;
  static OpenALSource* cmd_sound        = NULL;
  static OpenALSource* err_sound        = NULL;

  if (! activate_sound) {
    OpenALBuffer* activate   = MakeSoundAL (38);
    OpenALBuffer* deactivate = MakeSoundAL (39);
    OpenALBuffer* cmd_enter  = MakeSoundAL (9);
    OpenALBuffer* err_msg    = MakeSoundAL (37);

    activate_sound = cli_parse::states->sound_context->createSource ();
    activate_sound->attach (activate);

    deactivate_sound = cli_parse::states->sound_context->createSource ();
    deactivate_sound->attach (deactivate);

    cmd_sound = cli_parse::states->sound_context->createSource ();
    cmd_sound->attach (cmd_enter);

    err_sound = cli_parse::states->sound_context->createSource ();
    err_sound->attach (err_msg);
  }
  /* End Sound Loading */

  if (key.getType () == Epsilon::Keyboard::Misc) {
    // Toggle visibility of console on tab...
    if (key.getCode () == Epsilon::Keyboard::MiscKeys::Tab) {
      /* Get a hold of the keyboard object, so we can make use of modifier
           keys, modifying the behavior of the console toggle key when
             shift is held down. */
      Epsilon::Keyboard*               keyboard =
             states->window->win->getKeyboard ();
      const
      Epsilon::Keyboard::ModifierKeys* mod      =
             keyboard->getModifierKeys ();

      if (IsVisible ()) {
        // Hide the console if (and only if) shift is not down...
        if (! mod->getKey (Epsilon::Keyboard::ModifierKeys::Shift) && IsActive ()) {
          Hide       ();
          Deactivate ();

          /* Play Sound when Hiding Console. */
          deactivate_sound->play ();
        } else {
          if (IsActive ())
            Deactivate ();
          else
            Activate ();
        }
      }
      else {
        Show     ();
        Activate ();

        /* Play sound when the console pops up... */
        activate_sound->play ();
      }

      return false;
    }

    // Don't process keys if the console is not active...
    if (! (IsActive () && IsVisible ()))
      return false;

#define eKeyboard Epsilon::Keyboard

    eKeyboard*               keyboard =
           states->window->win->getKeyboard ();
    const
    eKeyboard::ModifierKeys* mod      =
           keyboard->getModifierKeys        ();

    bool ctrl  = mod->getKey (eKeyboard::ModifierKeys::Control);
    bool shift = mod->getKey (eKeyboard::ModifierKeys::Shift);

    switch (key.getCode ())
    {
      /* Key: UP ARROW */

      /* Cycle through commands, or scroll up, depending on the
            status of the Shift and Control keys... */
      case eKeyboard::MiscKeys::Up:
      {
        /* CTRL Down --> Scroll UP. */
        if (ctrl)
        {
          const int anchor_shift =
            ((shift) ? 5 : 1);

          anchor_line += anchor_shift;
        }

        /* If only shift is down, cycle through the command history... */
        else if (shift)
        {
          if (command_history.size ())
          {
            // Wrap around...
            if (last_command_num != 0)
              --last_command_num;
            else
              last_command_num = command_history.size () - 1;

            strcpy (command_line, command_history [last_command_num].c_str ());
          }
        }
      } break;

      /* Key: DOWN ARROW */

      /* Cycle through commands, or scroll down, depending on the
            status of the Shift and Control keys... */
      case eKeyboard::MiscKeys::Down:
      {
        /* CTRL Down --> Scroll DOWN. */
        if (ctrl) {
          const int anchor_shift =
            ((shift) ? 5 : 1);

          anchor_line -= anchor_shift;

          if (anchor_line < 0)
            anchor_line = 0;
        }

        /* If only shift is down, cycle through the command history... */
        else if (shift)
        {
          if (command_history.size ())
          {
            ++last_command_num;

            if (last_command_num >= command_history.size ())
              last_command_num = 0;

            if (command_history [last_command_num].c_str () != NULL) {
              strcpy (command_line, command_history [last_command_num].c_str ());
            }
          }
        }
      } break;

      /* Key: LEFT  -  Move cursor to the left. */
      case eKeyboard::MiscKeys::Left:
        cursor_pos++; /* Cursor pos: 0 is right-end, move left by addition.. */
        break;

      /* Key: RIGHT  -  Move cursor to the right. */
      case eKeyboard::MiscKeys::Right:
        if (cursor_pos != 0)
          cursor_pos--; /* Opposite of the comment in the case above... */
        break;

      /* Key: HOME  -  Move cursor pos to the beginning. */
      case eKeyboard::MiscKeys::Home:
        cursor_pos = strlen (command_line);
        break;

      /* Key: END  -  Move cursor pos to the end. */
      case eKeyboard::MiscKeys::End:
        cursor_pos = 0;
        break;
    }
  }

  // Don't process keys if the console is not active...
  if (! (IsActive () && IsVisible ()))
    return false;

  /* Ignore modifier keys when handling key repeats... */
  if (key.getType () != Epsilon::Keyboard::Modifier) {
    timer.tick ();

    last_key   = key;
    key_change = timer.milliseconds ();
  }

  if (key.getType () != Epsilon::Keyboard::Text) {
    switch (key.getCode ())
    {
      case Epsilon::Keyboard::MiscKeys::Enter:
      {
        /* Points to the first non-whitespace character
             found in (command_line). */
        char* cmd_line = first_alpha_num (command_line);

        /* If the command string was empty, then ignore it! */
        if (cmd_line == NULL)
          break;
///
///      Process Command Line
///
        eTB_CommandResult result =
          states->command->ProcessCommandLine (cmd_line);

        if (result.getStatus () != false) {
          /* Clear the command input line... */
          cursor_pos    = 0;
          *command_line = _T ('\0');

          /* Print the value of any variable we changed... */
          if (result.getVariable () != NULL) {
            const eTB_Variable* var = result.getVariable ();

            eTB_Printf ( "%s : %s\n",
                           result.getWord ().c_str (),
                             (char *)var->getValueString () );
          }

          /* Play Command Success Sound. */
          cmd_sound->play ();

          /* Store the re-joined command. */
          std::string str;

          /* Command has been split into two strings,
               join them back as needed... */
          if (result.getArgs ().size () > 0)
            str = result.getWord () + std::string (" ") + result.getArgs ();
          else
            str = result.getWord ();

          /* Push this command onto the command history. */
          command_history.push_back (str);
          last_command_num = command_history.size ();
        }else {
          if (result.getResult ().empty ())
            eTB_Printf (" >> Unknown Command or Variable: %s\n", cmd_line);
          else
            eTB_Printf (" >> Command Error: %s\n", result.getResult ().c_str ());

          /* Play Command Failure Sound. */
          err_sound->play ();
        }
      } break;

      case Epsilon::Keyboard::MiscKeys::Space:
        EditCommandLine (cursor_pos, command_line, ' ');
        break;

      case Epsilon::Keyboard::MiscKeys::Backspace:
      {
        EditCommandLine (cursor_pos, command_line, '\b');
      } break;
    }
    return false;
  }

  switch (key.getType ())
  {
    case Epsilon::Keyboard::Text:
    {
      /* Get a hold of the keyboard object, so we can make use of modifier
           keys, when mapping a key to its appropriate letter/symbol. */
      Epsilon::Keyboard*               keyboard =
             states->window->win->getKeyboard ();
      const
      Epsilon::Keyboard::ModifierKeys* mod      =
             keyboard->getModifierKeys ();

      EditCommandLine ( cursor_pos,
                          command_line,
                            Epsilon::Keyboard::getKeyChar (key, mod) );
    } break;

    default:
      eTB_Printf ("OnKeyPress (...)\n");
      break;
  }

  return false;
}



void
eTB_TextLine::InsertSpan (eTB_Text* pText)
{
  //assert (pText != NULL);
  //assert (! terminated_)
  spans_.push_back (pText);

  if (strstr (pText->GetMessage (), "\n"))
    terminated_ = true;
}

eTB_Text*
eTB_TextLine::getSpan (unsigned int idx) const
{
  if (idx >= spans_.size ())
    return NULL;

  return spans_ [idx];
}


eTB_Text::eTB_Text ( const char* szMessage,
                     DWORD       color,
                     eFont*      pFont )
{
//    message_ = eStringA (szMessage);
  message_ = strdup (szMessage);
  color_   = color;
  font_    = pFont;

  if (pFont == NULL)
  {
    /* Load the cnsole font if it is not already loaded... */
    if (states->r_console_font == NULL) {
      eFontInfo info;
      info.szName         = _tcsdup (_T ("Andale Mono"));
      info.numAlphaShades = 32;
      info.isBold         = false;
      info.isItalic       = false;
      info.isUnderline    = false;
      info.glyphSize      = 16;

      states->r_console_font = new eFont ();
      states->r_console_font->loadFont (&info);
    }

    font_ = states->r_console_font;
  }
}
